# Copyright (c) 2025 Linx Software, Inc.
#
# xlin-sbom-analysis tool is licensed under Mulan PSL v2.

# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#
# http://license.coscl.org.cn/MulanPSL2
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.

import requests
from cvss import CVSS3, CVSS4

def _get_severity_level(raw_score):
    """
    解析原始评分字符串并返回格式化的严重程度等级和分数

    Args:
        raw_score (str): 原始的CVSS评分字符串，可能包含CVSS版本信息和评分数据

    Returns:
        str: 格式化的严重程度等级和分数，格式为"等级 (分数)"，如果解析失败则返回原始字符串
    """

    if isinstance(raw_score, str) and raw_score != "N/A":
        if raw_score.startswith("CVSS:3.0") or raw_score.startswith("CVSS:3.1"):
            try:
                cvss = CVSS3(raw_score)
                severity = cvss.severities()[0]
                score_value = cvss.scores()[0]
                severity_level = f"{severity} ({score_value})"
            except:
                severity_level = raw_score
        elif raw_score.startswith("CVSS:4.0"):
            try:
                cvss = CVSS4(raw_score)
                severity_level = f"{cvss.severity} ({cvss.base_score})"
            except:
                severity_level = raw_score
        else:
            severity_level = raw_score
    else:
        severity_level = str(raw_score)

    return severity_level


def query_osv_vulnerability(package_name, version, config=None):
    """
    调用OSV API查询指定组件版本的漏洞信息

    Args:
        package_name (str): 组件名称
        version (str): 组件版本
        config (dict, optional): 配置参数。默认为{}

    Returns:
        dict: JSON格式的漏洞信息，如果出错则抛出RequestException异常

    Raises:
        requests.exceptions.RequestException: 当API请求失败时抛出异常
    """
    
    if config is None:
        config = {}
    vuln_debug = config.get("general", {}).get('debug_mode', {}).get('vulnerability', {})

    if vuln_debug.get('enabled') and vuln_debug.get('skip'):
        return {}

    url = "https://api.osv.dev/v1/query"
    payload = {
        "package": {
            "name": package_name
        },
        "version": version
    }
    headers = {"Content-Type": "application/json"}

    try:
        response = requests.post(url, json=payload, headers=headers)
        response.raise_for_status()  # 检查HTTP错误
        return response.json()
    except requests.exceptions.RequestException as e:
        raise requests.exceptions.RequestException(f"查询漏洞信息失败: {e}")


def process_osv_vuln(vuln, package_name):
    """
    处理OSV漏洞数据，提取指定软件包的漏洞信息

    Args:
        vuln (dict): OSV API返回的单个漏洞信息字典
        package_name (str): 软件包名称，用于匹配受影响的包

    Returns:
        tuple: 包含四个元素的元组
            - vuln_id (str): 漏洞ID，优先选择CVE编号
            - severity_type (str): 漏洞评分类型，如CVSS等，未找到则为"N/A"
            - severity_level (str): 漏洞评分等级和分数，未找到则为"N/A"
            - fixed (str): 修复版本，未找到则为"N/A"
    """

    vuln_id_candidate = vuln["id"]
    aliases_list = vuln.get("aliases", [])

    # 检查id本身是否以"CVE"开头
    if vuln_id_candidate.startswith("CVE"):
        vuln_id = vuln_id_candidate
    else:
        # 检查aliases中是否有以"CVE"开头的
        cve_aliases = [
            alias for alias in aliases_list if alias.startswith("CVE")]
        if cve_aliases:
            vuln_id = cve_aliases[0]
        else:
            vuln_id = vuln_id_candidate

    # 初始化默认值
    severity_type = "N/A"
    severity_level = "N/A"
    fixed = 'N/A'

    # 查询vuln下的漏洞严重程度信息
    if "severity" in vuln and vuln["severity"]:
        first_severity = vuln["severity"][0]
        severity_type = first_severity.get("type", "N/A")
        raw_score = first_severity.get("score", "N/A")
        severity_level = _get_severity_level(raw_score)

    # 遍历affected字段寻找匹配的包
    if "affected" in vuln:
        for affected in vuln["affected"]:
            # 检查package字段是否存在且name匹配
            if "package" in affected and affected["package"].get("name") == package_name:
                # 获取fixed版本
                if "ranges" in affected and affected["ranges"]:
                    first_range = affected["ranges"][0]
                    if "events" in first_range:
                        # 遍历所有事件，记录最后一个fixed版本
                        for event in first_range["events"]:
                            if "fixed" in event:
                                fixed = event["fixed"]

                # 查询affected字段下的严重程度信息
                if severity_level == "N/A" and "severity" in affected and affected["severity"]:
                    first_severity = affected["severity"][0]
                    severity_type = first_severity.get("type", "N/A")
                    raw_score = first_severity.get("score", "N/A")
                    severity_level = _get_severity_level(raw_score)

                break

    return vuln_id, severity_type, severity_level, fixed
